/*
 * @Copyright: 2017 www.yyfax.com Inc. All rights reserved.
 */
package com.yyfax.pay.third.facade.fuiou;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;

import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.fuiou.mpay.encrypt.DESCoderFUIOU;
import com.fuiou.util.MD5;
import com.google.common.base.Joiner;
import com.yyfax.commons.http.HttpUtil;
import com.yyfax.commons.json.JacksonUtil;
import com.yyfax.framework.common.Log;
import com.yyfax.pay.common.enums.PayRspCodeEnum;
import com.yyfax.pay.common.exception.PayExceptionCode;
import com.yyfax.pay.common.exception.YYPayException;
import com.yyfax.pay.third.model.to.fuiou.FuiouBaseReqVO;
import com.yyfax.pay.third.model.to.fuiou.PayOrderFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.PayOrderFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.PayoutOrderFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.PlaceOrderFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.PlaceOrderFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPayOrderAgainFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPayOrderAgainFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPayOrderFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPayOrderFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPlaceOrderAgainFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPlaceOrderAgainFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPlaceOrderAgainRetryFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPlaceOrderAgainRetryFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPlaceOrderFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPlaceOrderFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPlaceOrderRetryFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.ProtocolPlaceOrderRetryFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.QueryOrderByFuiouOrderNumContent;
import com.yyfax.pay.third.model.to.fuiou.QueryOrderByFuiouOrderNumParams;
import com.yyfax.pay.third.model.to.fuiou.QueryOrderByOrderNumFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.QueryOrderByOrderNumFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.QueryPayoutOrderFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.WithoutVerifyFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.WithoutVerifyFuiouParams;
import com.yyfax.pay.third.model.to.fuiou.xml.PayoutOrderFuiouContent;
import com.yyfax.pay.third.model.to.fuiou.xml.QueryPayoutOrderFuiouContent;
import com.yyfax.pay.third.model.to.yyfax.PayoutDataBinder;

/**
 * 富友支付API
 * 
 * @author heizq
 * @date 2017-04-11 10:39:53
 * @since v1.0.0
 */
@Service
public class FuiouApi {

	private static Logger logger = LoggerFactory.getLogger(FuiouApi.class);

	private static final int CONNECT_TIMEOUT = 30000;
	private static final int READ_TIMEOUT = 30000;

	/**
	 * 商户下单、验证码获取接口
	 * 
	 * @author heizq
	 * @date 2017-04-11 18:08:25
	 * @since v1.0.0
	 * @param placeOrderFuiouParams
	 * @return
	 * @throws Throwable
	 */
	public PlaceOrderFuiouContent placeOrder(PlaceOrderFuiouParams placeOrderFuiouParams) throws Exception {
		String userId = null;
		try {
			userId = placeOrderFuiouParams.getUserId();
			logger.info("【pay】【FUIOU】userId:{},获取验证码请求信息:{}", userId, placeOrderFuiouParams.toString());

			String respStr = HttpUtil.postFormDirect(placeOrderFuiouParams.getUrl(), placeOrderFuiouParams.getParams(), null, null, null, null, null,
					CONNECT_TIMEOUT, READ_TIMEOUT);
			logger.info("【pay】【FUIOU】userId:{},获取验证码返回解析前数据：{}", userId, respStr);

			if (StringUtils.isNotBlank(respStr)) {
				respStr = DESCoderFUIOU.desDecrypt(respStr, DESCoderFUIOU.getKeyLength8(placeOrderFuiouParams.getMerchantSecurit()));
				logger.info("userId:{},获取验证码返回解析后数据：{}", userId, respStr);
				PlaceOrderFuiouContent content = PlaceOrderFuiouContent.fromXml(respStr);
				content.setRsqContent(respStr);
				return content;
			} else {
				// 如果http请求返回null，返回请求具体详情，此处需要处理
				logger.info("【pay】【FUIOU】获取验证码订单信息：{}，获取验证码请求失败，或者返回值为空(默认响应码为超时)", placeOrderFuiouParams.getParams());
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "查单请求返回为空");
			}

		} catch (SocketTimeoutException e) {
			logger.error("【pay】【FUIOU】获取验证码请求超时,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "获取验证码请求超时");
		} catch (BadPaddingException e) {
			logger.error("【pay】【FUIOU】获取验证码加解密失败：,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "获取验证码解密失败");
		}
	}

	/**
	 * 独立鉴权-直连
	 *
	 * @param withoutVerifyFuiouParams
	 * @return
	 * @author xulqb
	 * @date 2017/12/21 15:11
	 * @since V1.0.0
	 */
	public WithoutVerifyFuiouContent withoutVerify(WithoutVerifyFuiouParams withoutVerifyFuiouParams) throws Exception {
		String userId = null;
		try {
			userId = withoutVerifyFuiouParams.getUserId();
			logger.info(Log.op("FuiouApi.withoutVerify").msg("独立鉴权请求富有报文").kv("withoutVerifyFuiouParams", withoutVerifyFuiouParams).toString());
			logger.info(Log.op("FuiouApi.withoutVerify").msg("独立鉴权请求富有报文Params").kv("Params", withoutVerifyFuiouParams.getParams()).toString());
			String respStr = HttpUtil.postFormDirect(withoutVerifyFuiouParams.getUrl(), withoutVerifyFuiouParams.getParams(), null, null, "UTF-8",
					null, null, CONNECT_TIMEOUT, READ_TIMEOUT);
			logger.info(Log.op("FuiouApi.withoutVerify").msg("请求富有独立鉴权返回数据").kv("respStr", respStr).toString());
			if (StringUtils.isNotBlank(respStr)) {
				WithoutVerifyFuiouContent content = WithoutVerifyFuiouContent.fromXml(respStr);
				return content;
			} else {
				// 如果http请求返回null，返回请求具体详情，此处需要处理
				logger.info(Log.op("FuiouApi.withoutVerify").msg("独立鉴权请求失败").kv("respStr", respStr).toString());
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "独立鉴权请求失败,请求返回为空");
			}

		} catch (SocketTimeoutException e) {
			logger.error(Log.op("FuiouApi.withoutVerify").msg("独立鉴权请求富有接口超时").kv("withoutVerifyFuiouParams", withoutVerifyFuiouParams).toString(), e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "独立鉴权请求富有接口超时");
		}
	}

	/**
	 * 商户协议再次下单、验证码获取接口
	 * 
	 * @author heizq
	 * @date 2017-05-09 09:26:59
	 * @since v1.2.0
	 * @param placeOrderFuiouParams
	 * @return
	 * @throws Exception
	 */
	public ProtocolPlaceOrderAgainFuiouContent protocolPlaceOrderAgain(ProtocolPlaceOrderAgainFuiouParams placeOrderFuiouParams) throws Exception {
		String userId = "";
		try {
			userId = placeOrderFuiouParams.getUserId();
			logger.debug("【pay】【FUIOU RP】userId:{},获取验证码请求信息:{}", userId, JSON.toJSONString(placeOrderFuiouParams));
			String respStr = HttpUtil.post(placeOrderFuiouParams.getUrl(), placeOrderFuiouParams.getParams());
			logger.info("【pay】【FUIOU RP】userId:{},获取验证码返回解析前数据：{}", userId, respStr);

			if (StringUtils.isNotBlank(respStr)) {
				respStr = DESCoderFUIOU.desDecrypt(respStr, DESCoderFUIOU.getKeyLength8(placeOrderFuiouParams.getMerchantSecurit()));
				logger.info("【pay】【FUIOU RP】userId:{},获取验证码返回解析后数据：{}", userId, respStr);
				ProtocolPlaceOrderAgainFuiouContent content = ProtocolPlaceOrderAgainFuiouContent.fromXml(respStr);
				content.setRspContent(respStr);
				return content;
			} else {
				// 如果http请求返回null，返回请求具体详情，此处需要处理
				logger.info("【pay】【FUIOU RP】获取验证码订单信息：{}，获取验证码请求失败，或者返回值为空(默认响应码为超时)", JSON.toJSONString(placeOrderFuiouParams));
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "获取验证码返回null");
			}
		} catch (SocketTimeoutException e) {
			logger.error("【pay】【FUIOU P】获取验证码请求超时,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "获取验证码请求超时");
		} catch (BadPaddingException e) {
			logger.error("【pay】【FUIOU P】获取验证码加解密失败：,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "获取验证码解密失败");
		}
	}

	/**
	 * 商户协议首次下单、验证码获取接口
	 * 
	 * @author heizq
	 * @date 2017-05-09 09:26:59
	 * @since v1.2.0
	 * @param placeOrderFuiouParams
	 * @return
	 * @throws Exception
	 */
	public ProtocolPlaceOrderFuiouContent protocolPlaceOrder(ProtocolPlaceOrderFuiouParams placeOrderFuiouParams) throws Exception {
		placeOrderFuiouParams.validate();
		String userId = "";
		try {
			userId = placeOrderFuiouParams.getUserId();
			logger.debug("【pay】【FUIOU P】userId:{},获取验证码请求信息:{}", userId, JSON.toJSONString(placeOrderFuiouParams));

			String respStr = HttpUtil.post(placeOrderFuiouParams.getUrl(), placeOrderFuiouParams.getParams());
			logger.info("【pay】【FUIOU P】userId:{},获取验证码返回解析前数据：{}", userId, respStr);

			if (StringUtils.isNotBlank(respStr)) {
				respStr = DESCoderFUIOU.desDecrypt(respStr, DESCoderFUIOU.getKeyLength8(placeOrderFuiouParams.getMerchantSecurit()));
				logger.info("userId:{},获取验证码返回解析后数据：{}", userId, respStr);
				ProtocolPlaceOrderFuiouContent content = ProtocolPlaceOrderFuiouContent.fromXml(respStr);
				content.setRspContent(respStr);
				return content;
			} else {
				// 如果http请求返回null，返回请求具体详情，此处需要处理
				logger.info("【pay】【FUIOU RP】获取验证码订单信息：{}，获取验证码请求失败，或者返回值为空(默认响应码为超时)", JSON.toJSONString(placeOrderFuiouParams));
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "获取验证码返回null");
			}
		} catch (SocketTimeoutException e) {
			logger.error("【pay】【FUIOU P】获取验证码请求超时,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "获取验证码请求超时");
		} catch (BadPaddingException e) {
			logger.error("【pay】【FUIOU P】获取验证码加解密失败：,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "获取验证码解密失败");
		}
	}

	/**
	 * 商户协议首次下单重发验证码获取接口
	 * 
	 * @author heizq
	 * @date 2017-05-09 09:26:59
	 * @since v1.2.0
	 * @param placeOrderFuiouParams
	 * @return
	 * @throws Exception
	 */
	public ProtocolPlaceOrderRetryFuiouContent protocolPlaceOrderRetry(ProtocolPlaceOrderRetryFuiouParams placeOrderFuiouParams) throws Exception {
		String userId = "";
		try {
			userId = placeOrderFuiouParams.getUserId();
			logger.debug("【pay】【FUIOU P】userId:{},获取验证码请求信息:{}", userId, JSON.toJSONString(placeOrderFuiouParams));

			String respStr = HttpUtil.post(placeOrderFuiouParams.getUrl(), placeOrderFuiouParams.getParams());
			logger.info("【pay】【FUIOU P】userId:{},获取验证码返回解析前数据：{}", userId, respStr);

			if (StringUtils.isNotBlank(respStr)) {
				respStr = DESCoderFUIOU.desDecrypt(respStr, DESCoderFUIOU.getKeyLength8(placeOrderFuiouParams.getMerchantSecurit()));
				logger.info("userId:{},获取验证码返回解析后数据：{}", userId, respStr);
				ProtocolPlaceOrderRetryFuiouContent content = ProtocolPlaceOrderRetryFuiouContent.fromXml(respStr);
				content.setRsqContent(respStr);
				return content;
			} else {
				// 如果http请求返回null，返回请求具体详情，此处需要处理
				logger.info("【pay】【FUIOU RP】获取验证码订单信息：{}，获取验证码请求失败，或者返回值为空(默认响应码为超时)", JSON.toJSONString(placeOrderFuiouParams));
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "获取验证码返回null");
			}
		} catch (SocketTimeoutException e) {
			logger.error("【pay】【FUIOU P】获取验证码请求超时,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "获取验证码请求超时");
		} catch (BadPaddingException e) {
			logger.error("【pay】【FUIOU P】获取验证码加解密失败：,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "获取验证码解密失败");
		}
	}

	/**
	 * 商户协议再次下单重发验证码获取接口
	 * 
	 * @author heizq
	 * @date 2017-05-09 09:26:59
	 * @since v1.2.0
	 * @param placeOrderFuiouParams
	 * @return
	 * @throws Exception
	 */
	public ProtocolPlaceOrderAgainRetryFuiouContent protocolPlaceOrderAgainRetry(ProtocolPlaceOrderAgainRetryFuiouParams placeOrderFuiouParams)
			throws Exception {
		String userId = "";
		try {
			userId = placeOrderFuiouParams.getUserId();
			logger.debug("【pay】【FUIOU P】userId:{},获取验证码请求信息:{}", userId, JSON.toJSONString(placeOrderFuiouParams));

			String respStr = HttpUtil.post(placeOrderFuiouParams.getUrl(), placeOrderFuiouParams.getParams());
			logger.info("【pay】【FUIOU P】userId:{},获取验证码返回解析前数据：{}", userId, respStr);

			if (StringUtils.isNotBlank(respStr)) {
				respStr = DESCoderFUIOU.desDecrypt(respStr, DESCoderFUIOU.getKeyLength8(placeOrderFuiouParams.getMerchantSecurit()));
				logger.info("userId:{},获取验证码返回解析后数据：{}", userId, respStr);
				ProtocolPlaceOrderAgainRetryFuiouContent content = ProtocolPlaceOrderAgainRetryFuiouContent.fromXml(respStr);
				content.setRsqContent(respStr);
				return content;
			} else {
				// 如果http请求返回null，返回请求具体详情，此处需要处理
				logger.info("【pay】【FUIOU RP】获取验证码订单信息：{}，获取验证码请求失败，或者返回值为空(默认响应码为超时)", JSON.toJSONString(placeOrderFuiouParams));
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "获取验证码返回null");
			}
		} catch (SocketTimeoutException e) {
			logger.error("【pay】【FUIOU P】获取验证码请求超时,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "获取验证码请求超时");
		} catch (BadPaddingException e) {
			logger.error("【pay】【FUIOU P】获取验证码加解密失败：,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "获取验证码解密失败");
		}
	}

	/**
	 * 支付接口
	 * 
	 * @author heizq
	 * @date 2017-04-11 18:12:01
	 * @since v1.0.0
	 * @param payOrderFuiouParams
	 * @return
	 * @throws Throwable
	 */
	public PayOrderFuiouContent pay(PayOrderFuiouParams payOrderFuiouParams) throws Exception {
		String userId = null;
		try {
			userId = payOrderFuiouParams.getUserId();
			logger.debug("【pay】【FUIOU】userId:{},支付请求信息:{}", userId, payOrderFuiouParams.toString());

			String respStr = HttpUtil.postFormDirect(payOrderFuiouParams.getUrl(), payOrderFuiouParams.getParams(), null, null, null, null, null,
					CONNECT_TIMEOUT, READ_TIMEOUT);
			logger.info("【pay】【FUIOU】userId:{},支付返回解析前数据：{}", userId, respStr);

			if (StringUtils.isNotBlank(respStr)) {
				respStr = DESCoderFUIOU.desDecrypt(respStr, DESCoderFUIOU.getKeyLength8(payOrderFuiouParams.getMerchantSecurit()));
				logger.info("【pay】【FUIOU】userId:{},支付返回解析后数据：{}", userId, respStr);
				PayOrderFuiouContent payOrderFuiouContent = PayOrderFuiouContent.fromXml(respStr);
				payOrderFuiouContent.setRsqContent(respStr);
				return payOrderFuiouContent;
			} else {
				// 如果http请求返回null，返回请求具体详情，此处需要处理
				logger.info("【pay】【FUIOU】订单信息：{}，支付请求失败，或者返回值为空(默认响应码为超时)", payOrderFuiouParams.toString());
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "查单请求返回为空");
			}

		} catch (SocketTimeoutException e) {
			logger.error("【pay】【FUIOU】获取验证码请求超时,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "支付处理中，请稍后");
		} catch (BadPaddingException e) {
			logger.error("【pay】【FUIOU】支付加解密失败,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "支付解密失败");
		}
	}

	/**
	 * 首次协议支付接口
	 * 
	 * @author heizq
	 * @date 2017-05-09 19:22:55
	 * @since v1.2.0
	 * @param payOrderFuiouParams
	 * @return
	 * @throws Exception
	 */
	public ProtocolPayOrderFuiouContent protocolPay(ProtocolPayOrderFuiouParams protocolPayOrderFuiouParams) throws Exception {
		protocolPayOrderFuiouParams.validate();
		String userId = "";
		try {
			userId = protocolPayOrderFuiouParams.getUserId();
			logger.debug("【pay】【FUIOU】userId:{},支付请求信息:{}", userId, JSON.toJSONString(protocolPayOrderFuiouParams));

			String respStr = HttpUtil.postFormDirect(protocolPayOrderFuiouParams.getUrl(), protocolPayOrderFuiouParams.getParams(), null, null, null,
					null, null, CONNECT_TIMEOUT, READ_TIMEOUT);
			logger.info("【pay】【FUIOU】userId:{},支付返回解析前数据：{}", userId, respStr);

			if (StringUtils.isNotBlank(respStr)) {
				respStr = DESCoderFUIOU.desDecrypt(respStr, DESCoderFUIOU.getKeyLength8(protocolPayOrderFuiouParams.getMerchantSecurit()));
				logger.info("【pay】【FUIOU】userId:{},支付返回解析后数据：{}", userId, respStr);
				ProtocolPayOrderFuiouContent payOrderFuiouContent = ProtocolPayOrderFuiouContent.fromXml(respStr);
				payOrderFuiouContent.setRsqContent(respStr);
				return payOrderFuiouContent;
			} else {
				// 如果http请求返回null，默认为超时异常，如果后期重构HttpUtil方式，返回请求具体详情，此处需要处理
				logger.info("【pay】【FUIOU】订单信息：{}，支付请求失败，或者返回值为空(默认响应码为超时)", JSON.toJSON(protocolPayOrderFuiouParams));
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "支付请求返回null");
			}
		} catch (SocketTimeoutException e) {
			logger.error("【pay】【FUIOU】获取验证码请求超时,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "支付处理中，请稍后");
		} catch (BadPaddingException e) {
			logger.error("【pay】【FUIOU】支付加解密失败,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "支付解密失败");
		}
	}

	/**
	 * 再次协议支付接口
	 * 
	 * @author heizq
	 * @date 2017-05-09 19:22:55
	 * @since v1.2.0
	 * @param payOrderFuiouParams
	 * @return
	 * @throws Exception
	 */
	public ProtocolPayOrderAgainFuiouContent reProtocolPay(ProtocolPayOrderAgainFuiouParams protocolPayOrderFuiouParams) throws Exception {
		protocolPayOrderFuiouParams.validate();
		String userId = "";
		try {
			userId = protocolPayOrderFuiouParams.getUserId();
			logger.debug("【pay】【FUIOU】userId:{},支付请求信息:{}", userId, JSON.toJSONString(protocolPayOrderFuiouParams));

			String respStr = HttpUtil.post(protocolPayOrderFuiouParams.getUrl(), protocolPayOrderFuiouParams.getParams());
			logger.info("【pay】【FUIOU】userId:{},支付返回解析前数据：{}", userId, respStr);

			if (StringUtils.isNotBlank(respStr)) {
				respStr = DESCoderFUIOU.desDecrypt(respStr, DESCoderFUIOU.getKeyLength8(protocolPayOrderFuiouParams.getMerchantSecurit()));
				logger.info("【pay】【FUIOU】userId:{},支付返回解析后数据：{}", userId, respStr);
				ProtocolPayOrderAgainFuiouContent payOrderFuiouContent = ProtocolPayOrderAgainFuiouContent.fromXml(respStr);
				payOrderFuiouContent.setRsqContent(respStr);
				return payOrderFuiouContent;
			} else {
				// 如果http请求返回null，默认为超时异常，如果后期重构HttpUtil方式，返回请求具体详情，此处需要处理
				logger.info("【pay】【FUIOU】订单信息：{}，支付请求失败，或者返回值为空(默认响应码为超时)", JSON.toJSON(protocolPayOrderFuiouParams));
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "支付请求返回null");
			}
		} catch (SocketTimeoutException e) {
			logger.error("【pay】【FUIOU】支付请求超时,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "支付请求超时");
		} catch (BadPaddingException e) {
			logger.error("【pay】【FUIOU】支付加解密失败,userId:" + userId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "支付解密失败");
		}
	}

	public PayoutOrderFuiouContent payout(PayoutOrderFuiouParams payOrderFuiouParams) throws Exception {
		payOrderFuiouParams.validate();
		String merchantCode = payOrderFuiouParams.getMerchantCode();
		// String merchantId = payOrderFuiouParams.getMerchantId();
		try {
			String xml = payOrderFuiouParams.getPayOutReqData().toXml();
			// logger.info("【【FUIOU】【代付】merchant:{},单笔代付请求信息{}", merchantCode, xml);

			Map<String, String> param = new HashMap<String, String>();
			param.put("merid", payOrderFuiouParams.getMerchantId());
			param.put("reqtype", payOrderFuiouParams.getReqType());
			param.put("xml", xml);
			param.put("mac", MD5.MD5Encode(Joiner.on(PayoutOrderFuiouParams.ENCRYPTSEPERATOR).join(payOrderFuiouParams.getMerchantId(),
					payOrderFuiouParams.getMerchantSecurit(), payOrderFuiouParams.getReqType(), xml)));

			logger.info("【FUIOU】【代付】merchantCode:{},单笔代付请求信息:{}", merchantCode, param);

			// String respStr = HttpUtil.post(payOrderFuiouParams.getUrl(), param);
			String respStr = HttpUtil.postFormDirect(payOrderFuiouParams.getUrl(), param, null, null, CharEncoding.UTF_8, null, null, CONNECT_TIMEOUT,
					READ_TIMEOUT);

			logger.info("【FUIOU】【代付】merchantCode:{},单笔代付返回数据：{}", merchantCode, respStr);

			if (StringUtils.isNotBlank(respStr)) {
				PayoutOrderFuiouContent content = PayoutOrderFuiouContent.fromXml(respStr);
				content.setReqContent(JacksonUtil.nonullMapper.toJson(param));
				return content;
			} else {
				logger.error("【FUIOU】【代付】merchantCode:{},单笔代付失败", merchantCode);
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAYOUT_FAIL.getValue(), "支付失败");
			}
		} catch (SocketTimeoutException e) {
			logger.error("【FUIOU】【代付】请求出现超时,请求payOrderFuiouParams：" + payOrderFuiouParams);
			// 请求超时
			throw e;
		} catch (Exception e) {
			logger.error("【FUIOU】【代付】merchantCode:" + merchantCode + ",支付请求失败", e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAYOUT_FAIL.getValue(), "支付失败");
		}
	}

	/**
	 * 发起代付请求
	 * 
	 * @author buyi
	 * @date 2018-05-12 02:02:46
	 * @since v2.1.5
	 * @param payoutOrderParams
	 * @param binder
	 * @return
	 * @throws Exception
	 */
	public PayoutOrderFuiouContent payoutNew(PayoutOrderFuiouParams payOrderFuiouParams, PayoutDataBinder binder) throws Exception {
		final String op = "FuiouApi.payoutNew";
		payOrderFuiouParams.validate();

		String xml = payOrderFuiouParams.getPayOutReqData().toXml();

		Map<String, String> param = new HashMap<String, String>();
		param.put("merid", payOrderFuiouParams.getMerchantId());
		param.put("reqtype", payOrderFuiouParams.getReqType());
		param.put("xml", xml);
		param.put("mac", MD5.MD5Encode(Joiner.on(PayoutOrderFuiouParams.ENCRYPTSEPERATOR).join(payOrderFuiouParams.getMerchantId(),
				payOrderFuiouParams.getMerchantSecurit(), payOrderFuiouParams.getReqType(), xml)));

		logger.info(Log.op(op).msg("富友支付 | 代付 | 发送http请求").kv("payoutOrderId", binder.getBusiId()).kv("param", param).toString());

		binder.setReqContent(ToStringBuilder.reflectionToString(param, ToStringStyle.SHORT_PREFIX_STYLE));
		binder.setReqUrl(payOrderFuiouParams.getUrl());
		binder.setReqDate(new Date());
		
		String respStr = HttpUtil.postFormDirect(payOrderFuiouParams.getUrl(), param, null, null, CharEncoding.UTF_8, null, null, CONNECT_TIMEOUT,
				READ_TIMEOUT);

		logger.info(Log.op(op).msg("富友支付 | 代付 | http请求响应").kv("payoutOrderId", binder.getBusiId()).kv("rsp", respStr).toString());
		binder.setRspContent(respStr);

		if (StringUtils.isBlank(respStr)) {
			// 如果富友代付响应报文为空
			logger.info(Log.op(op).msg("富友支付 | 代付 | http请求响应报文为空").kv("payoutOrderId", binder.getBusiId()).toString());

			// 报文为空为未知状态，返回未知状态
			throw PayExceptionCode.WARN_UNKNOWN.exp();
		}

		// 如果报文不为空，则解析返回报文
		PayoutOrderFuiouContent content = PayoutOrderFuiouContent.fromXml(respStr);
		return content;
	}

	/**
	 * 通过富友订单号查询订单结果（这个暂时不用）
	 * 
	 * @author heizq
	 * @date 2017-04-11 18:21:09
	 * @since v1.0.0
	 * @param queryOrderByFuiouOrderNumParams
	 * @return
	 * @throws Throwable
	 */
	public QueryOrderByFuiouOrderNumContent searchOrderByFyOrderId(QueryOrderByFuiouOrderNumParams queryOrderByFuiouOrderNumParams) throws Exception {
		logger.debug("【pay】【FUIOU】根据富友订单号查单参数:{}", JacksonUtil.nonullMapper.toJson(queryOrderByFuiouOrderNumParams));

		queryOrderByFuiouOrderNumParams.validate();

		try {
			String url = queryOrderByFuiouOrderNumParams.getUrl();

			logger.debug("【pay】【FUIOU】请求参数,订单号：{},FM：{}", queryOrderByFuiouOrderNumParams.getFuiouOrderNum(),
					queryOrderByFuiouOrderNumParams.getParam());

			Map<String, String> params = new HashMap<String, String>();
			params.put("FM", queryOrderByFuiouOrderNumParams.getParam());
			String respStr = HttpUtil.post(url, params);
			logger.info("【pay】【FUIOU】订单号：{},返回数据：{}", queryOrderByFuiouOrderNumParams.getFuiouOrderNum(), respStr);

			if (StringUtils.isNotBlank(respStr)) {
				return QueryOrderByFuiouOrderNumContent.fromXml(respStr);
			} else {
				logger.error("【pay】【FUIOU】订单号：{},通过富友订单号查询订单结果失败", queryOrderByFuiouOrderNumParams.getFuiouOrderNum());
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "通过富友订单号查询订单结果失败");
			}
		} catch (Exception e) {
			logger.error("【pay】【FUIOU】通过富友订单号查询订单结果失败,订单号：" + queryOrderByFuiouOrderNumParams.getFuiouOrderNum(), e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue(), "通过富友订单号查询订单结果失败");
		}
	}

	/**
	 * 通过平台商户订单号查询订单结果
	 * 
	 * @author heizq
	 * @date 2017-04-11 18:22:01
	 * @since v1.0.0
	 * @param queryOrderByOrderNumFuiouParams
	 * @return
	 * @throws Throwable
	 */
	public QueryOrderByOrderNumFuiouContent searchOrderByMchtOrderId(QueryOrderByOrderNumFuiouParams queryOrderByOrderNumFuiouParams)
			throws Exception {
		try {
			String url = queryOrderByOrderNumFuiouParams.getUrl();
			logger.debug("【pay】【FUIOU】根据平台订单查单请求,订单号：{}，参数:{}", queryOrderByOrderNumFuiouParams.getOrderId(),
					queryOrderByOrderNumFuiouParams.getParams());

			String respStr = HttpUtil.postFormDirect(url, queryOrderByOrderNumFuiouParams.getParams(), null, null, null, null, null, CONNECT_TIMEOUT,
					READ_TIMEOUT);
			logger.info("【pay】【FUIOU】查单请求订单号{}，返回数据: {}", queryOrderByOrderNumFuiouParams.getOrderId(), respStr);

			if (StringUtils.isNotBlank(respStr)) {
				QueryOrderByOrderNumFuiouContent content = QueryOrderByOrderNumFuiouContent.fromXml(respStr);
				content.setRspContent(respStr);
				return content;
			} else {
				// 如果http请求返回null，返回请求具体详情，此处需要处理
				logger.info("【pay】【FUIOU】订单号：{}，查询订单超时，或者返回值为空，则默认响应码为超时", queryOrderByOrderNumFuiouParams.getOrderId());
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_UNKNOWN.getValue(), "查单请求返回为空");
			}
		} catch (SocketTimeoutException e) {
			logger.error("【pay】【FUIOU】查单请求超时,orderId:" + queryOrderByOrderNumFuiouParams.getOrderId(), e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.getValue(), "支付请求超时");
		}
	}

	public QueryPayoutOrderFuiouContent queryPayOutOrder(QueryPayoutOrderFuiouParams params) throws Exception {
		params.validate();
		String merchantCode = params.getMerchantCode();
		String merchantId = params.getMerchantId();
		try {
			String xml = params.getQueryPayoutReqData().toXml();
			logger.info("【pay】【FUIOU】userId:{},查询请求信息:{}", merchantCode, merchantId, xml);
			Map<String, String> param = new HashMap<String, String>();
			param.put("merid", params.getMerchantId());
			param.put("reqtype", params.getReqType());
			param.put("xml", xml);
			param.put("mac", MD5.MD5Encode(Joiner.on(PayoutOrderFuiouParams.ENCRYPTSEPERATOR).join(params.getMerchantId(),
					params.getMerchantSecurit(), params.getReqType(), xml)));
			logger.debug("【pay】【FUIOU】userId:{},查询请求信息:{}", merchantCode, merchantId, param);
			String respStr = HttpUtil.post(params.getUrl(), param);
			logger.info("【pay】【FUIOU】userId:{},查询返回数据：{}", merchantCode, merchantId, respStr);
			if (StringUtils.isNotBlank(respStr)) {
				QueryPayoutOrderFuiouContent content = QueryPayoutOrderFuiouContent.fromXml(respStr);
				content.setReqContent(JacksonUtil.nonullMapper.toJson(param));
				return content;
			} else {
				logger.error("【pay】【FUIOU】userId:{},查询失败", merchantCode, merchantId);
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_QUERY_ORDER_FAIL.getValue(), "查询失败");
			}
		} catch (Exception e) {
			logger.error("【pay】【FUIOU】userId:{},查询失败", merchantCode, merchantId, e);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_QUERY_ORDER_FAIL.getValue(), "查询失败");
		}
	}

	/**
	 * 卡bin查询
	 * 
	 * @author buyi
	 * @date 2017-06-28 10:31:48
	 * @since v2.0.0
	 * @param cardBinReqVO
	 * @return
	 * @throws IOException
	 */
	public String cardBin(FuiouBaseReqVO reqVO) throws IOException {
		final String op = "FuiouApi.cardBin";
		logger.info(Log.op(op).msg("【富友】【卡bin】请求报文").kv("请求报文", reqVO.getReqBody()).toString());
		String rspStr = HttpUtil.postFormDirect(reqVO.getUrl(), reqVO.getActReqBody(), null, null, reqVO.getChartSet(), null, null, CONNECT_TIMEOUT,
				READ_TIMEOUT);

		logger.info(Log.op(op).msg("【富友】【卡bin】请求返回报文").kv("响应报文体", rspStr).toString());
		return rspStr;
	}

}
